25. Extra Credit: Add a List Header
L7 43 SC
This is an EXTRA CREDIT exercise, so complete it if you're up for a challenge!
In this exercise you'll add a header to your RecyclerView. It has lots of steps, but when you're done, your app will display a header as the first row of the list.
Let's finish the adapter using DataItems. You can use this more advanced pattern when you have more than two types of views to hold in a RecyclerView.
At the bottom of
SleepNightAdapter, add a sealed class calledDataItem:sealed class DataItem { data class SleepNightItem(val sleepNight: SleepNight): DataItem() { override val id = sleepNight.nightId }
}object Header: DataItem() { override val id = Long.MIN_VALUE } abstract val id: LongAdd the TextHolder class inside the
SleepNightAdapterclass.class TextViewHolder(view: View): RecyclerView.ViewHolder(view) { companion object { fun from(parent: ViewGroup): TextViewHolder { val layoutInflater = LayoutInflater.from(parent.context) val view = layoutInflater.inflate(R.layout.header, parent, false) return TextViewHolder(view) } } }Update the declaration of SleepNightAdapter to support any type of view holder.
class SleepNightAdapter:
ListAdapter<SleepNight, RecyclerView.ViewHolder>(SleepNightDiffCallback())
- Now let’s update
ListAdapterto hold a list ofDataIteminstead of a list ofSleepNight:
class SleepNightAdapter:
ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()) {}
Update your
DiffCallbackto handleDataIteminstead ofSleepNight:class SleepNightDiffCallback : DiffUtil.ItemCallback<DataItem>() { override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean { return oldItem.id == newItem.id }
}override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean { return oldItem == newItem }Then, to figure out what view type to return, add a check to see which type of item is in the list:
At the top of the file, before the class declaration, create variables for Header and SleepNight item types:
private val ITEM_VIEW_TYPE_HEADER = 0
private val ITEM_VIEW_TYPE_ITEM = 1
Then override getItemViewType and return the correct item view type.
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is DataItem.Header -> ITEM_VIEW_TYPE_HEADER
is DataItem.SleepNightItem -> ITEM_VIEW_TYPE_ITEM
}
}
Change onCreateViewHolder's return type to RecyclerView.ViewHolder, and update it to return the correct ITEM_VIEW_TYPE.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { ITEM_VIEW_TYPE_HEADER -> TextViewHolder.from(parent) ITEM_VIEW_TYPE_ITEM -> ViewHolder.from(parent) else -> throw ClassCastException("Unknown viewType ${viewType}") } }In
onBindViewHolder()you'll need to unwrap theDataItemto pass aSleepNightto theViewHolderoverride fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is ViewHolder -> { val nightItem = getItem(position) as DataItem.SleepNightItem holder.bind(nightItem.sleepNight, clickListener) } } }You'll need a way to convert a
List<SleepNight>to aList<DataItem>.private val adapterScope = CoroutineScope(Dispatchers.Default) fun addHeaderAndSubmitList(list: List<SleepNight>?) { adapterScope.launch { val items = when (list) { null -> listOf(DataItem.Header) else -> listOf(DataItem.Header) + list.map DataItem.SleepNightItem(it) } } withContext(Dispatchers.Main) { submitList(items) } } }Finally, update
SleepTrackerFragmentto pass a list ofDataIteminstead of a list ofSleepNightand call the new addHeaderAndSubmitList method instead of submitList method:sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer { it?.let { adapter.addHeaderAndSubmitList(it) } })
If you want to start at this step, you can download this exercise from: Step.13-Exercise-Add-a-List-Header.
You will find plenty of //TODO comments to help you complete this exercise, and if you get stuck, go back and watch the video again.
Once you’re done, you can check your solution against the solution we’ve provided here: Step.13-Solution-Add-a-List-Header, or using this git diff.
Task Description:
Complete these tasks to display a header as the first row of the list.
Task Feedback:
Wow, that was a lot of steps, great job!